#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "els.h"
#include "els_heap.h"
#include "els_func.h"
#include "els_gc.h"
#include "els_object.h"
#include "els_bytecode.h"
#include "els_vmhost.h"
#include "els_string.h"
#include "els_unit.h"
#include "els_vmcore.h"


#define els_API_index(l,index) Index(l, index)
extern void els_API_pushobject (els_VmObj *L, const StackObj *o);
#define	EXTRA_STACK	8



int els_Vmcore_tonumber (StackObj *obj) {
  if (ttype(obj) != ELS_TYPE_STRING)
    return 1;
  else {
    if (!els_Object_str2d(svalue(obj), &nvalue(obj)))
      return 2;
    ttype(obj) = ELS_TYPE_NUMBER;
    return 0;
  }
}


int els_Vmcore_tostring (els_VmObj *L, StackObj *obj) {  
  if (ttype(obj) != ELS_TYPE_NUMBER)
    return 1;
  else {
    char s[32];  
    els_number2str(s, nvalue(obj));  
    tsvalue(obj) = els_string_new(L, s);
    ttype(obj) = ELS_TYPE_STRING;
    return 0;
  }
}





static ElsCfunc *els_Vmcore_csfunciotn (els_VmObj *L, int nelems) {
  ElsCfunc *c = els_Function_new_csfunciotn(L, nelems);
  L->top -= nelems;
  while (nelems--)
    c->upvalue[nelems] = *(L->top+nelems);
  clvalue(L->top) = c;
  ttype(L->top) = ELS_TYPE_FUNCTION;
  incr_top;
  return c;
}


void els_Vmcore_C_csfunciotn (els_VmObj *L, els_CFunction c, int nelems) {
  ElsCfunc *cl = els_Vmcore_csfunciotn(L, nelems);
  cl->f.c = c;
  cl->isC = 1;
}


void els_Vmcore_L_csfunciotn (els_VmObj *L, els_ir_code *l, int nelems) {
  ElsCfunc *cl = els_Vmcore_csfunciotn(L, nelems);
  cl->f.l = l;
  cl->isC = 0;
}

const StackObj *els_Vmcore_getunit (els_VmObj *L, Els_vminline_obj t) {
  ElsCfunc *tm;
  int tg;
  if (ttype(t) == ELS_TYPE_UNIT &&  
      ((tg = hvalue(t)->htag) == ELS_TYPE_UNIT ||  
        els_unit_gettm(L, tg, TM_GETUNIT) == NULL)) { 
    
    const StackObj *h = els_Unit_get(L, hvalue(t), L->top-1);
    
    if (ttype(h) != ELS_TYPE_NULL || ((tm=els_unit_gettm(L, tg, TM_INDEX)) == NULL))
      return h;  
    
  }
  else {  
    tm = els_unit_gettmbyObj(L, t, TM_GETUNIT);
  }
  if (tm != NULL) {  
    els_Heap_checkstack(L, 2);
    *(L->top+1) = *(L->top-1);  
    *L->top = *t;  
    clvalue(L->top-1) = tm;  
    ttype(L->top-1) = ELS_TYPE_FUNCTION;
    L->top += 2;
    els_Heap_call(L, L->top - 3, 1);
    return L->top - 1;  
  }
  else {  
    els_error(L,"访问的变量不是对象");
    return NULL;  
  }
}

void els_Vmcore_setunit (els_VmObj *L, Els_vminline_obj t, Els_vminline_obj key) {
  int tg;
  if (ttype(t) == ELS_TYPE_UNIT &&  
      ((tg = hvalue(t)->htag) == ELS_TYPE_UNIT ||  
        els_unit_gettm(L, tg, TM_SETunit) == NULL)) 
    *els_Unit_set(L, hvalue(t), key) = *(L->top-1);  
  else {  
    ElsCfunc *tm = els_unit_gettmbyObj(L, t, TM_SETunit);
    if (tm != NULL) {
      els_Heap_checkstack(L, 3);
      *(L->top+2) = *(L->top-1);
      *(L->top+1) = *key;
      *(L->top) = *t;
      clvalue(L->top-1) = tm;
      ttype(L->top-1) = ELS_TYPE_FUNCTION;
      L->top += 3;
      els_Heap_call(L, L->top - 4, 0);  
    }

  }
}


const StackObj *els_Vmcore_getglobal (els_VmObj *L, TString *s) {
  const StackObj *value = els_Unit_getstr(L->gt, s);
  ElsCfunc *tm = els_unit_gettmbyObj(L, value, TM_GETGLOBAL);
  if (tm == NULL)  

    return value;  
  else {  
    els_Heap_checkstack(L, 3);
    clvalue(L->top) = tm;
    ttype(L->top) = ELS_TYPE_FUNCTION;
    tsvalue(L->top+1) = s;  
    ttype(L->top+1) = ELS_TYPE_STRING;
    *(L->top+2) = *value;
    L->top += 3;
    els_Heap_call(L, L->top - 3, 1);
    return L->top - 1;
  }
}


void els_Vmcore_setglobal (els_VmObj *L, TString *s) {
  const StackObj *oldvalue = els_Unit_getstr(L->gt, s);
  ElsCfunc *tm = els_unit_gettmbyObj(L, oldvalue, TM_SETGLOBAL);
  if (tm == NULL) {  
    if (oldvalue != &els_Object_nullobject) {
      
      *(StackObj *)oldvalue = *(L->top - 1);
    }
    else {
      StackObj key;
      ttype(&key) = ELS_TYPE_STRING;
      tsvalue(&key) = s;
      *els_Unit_set(L, L->gt, &key) = *(L->top - 1);
    }
  }
  else {
    els_Heap_checkstack(L, 3);
    *(L->top+2) = *(L->top-1);  
    *(L->top+1) = *oldvalue;
    ttype(L->top) = ELS_TYPE_STRING;
    tsvalue(L->top) = s;
    clvalue(L->top-1) = tm;
    ttype(L->top-1) = ELS_TYPE_FUNCTION;
    L->top += 3;
    els_Heap_call(L, L->top - 4, 0);
  }
}






static int els_Vmcore_strcomp (const TString *ls, const TString *rs) {
  const char *l = ls->str;
  size_t ll = ls->len;
  const char *r = rs->str;
  size_t lr = rs->len;
  for (;;) {
    int temp = strcoll(l, r);
    if (temp != 0) return temp;
    else {  
      size_t len = strlen(l);  
      if (len == ll)  
        return (len == lr) ? 0 : -1;  
      else if (len == lr)  
        return 1;  
      
      len++;
      l += len; ll -= len; r += len; lr -= len;
    }
  }
}


int els_Vmcore_lessthan (els_VmObj *L, const StackObj *l, const StackObj *r, Els_vminline_obj top) {
  if (ttype(l) == ELS_TYPE_NUMBER && ttype(r) == ELS_TYPE_NUMBER)
    return (nvalue(l) < nvalue(r));
  else if (ttype(l) == ELS_TYPE_STRING && ttype(r) == ELS_TYPE_STRING)
    return (els_Vmcore_strcomp(tsvalue(l), tsvalue(r)) < 0);
  else { 

    els_Heap_checkstack(L, 2);
    *top++ = *l;
    *top++ = *r;
    L->top--;
    return (ttype(L->top) != ELS_TYPE_NULL);
  }
}


void els_Vmcore_strconc (els_VmObj *L, int total, Els_vminline_obj top) {
  do {
    int n = 2;  
    if (tostring(L, top-2) || tostring(L, top-1)) {

    }
    else if (tsvalue(top-1)->len > 0) {  
      
      lint32 tl = (lint32)tsvalue(top-1)->len + 
                  (lint32)tsvalue(top-2)->len;
      char *buffer;
      int i;
      while (n < total && !tostring(L, top-n-1)) {  
        tl += tsvalue(top-n-1)->len;
        n++;
      }
      if (tl > MAX_SIZET) els_error(L, "字符串大小溢出");
      buffer = els_Object_openspace(L, tl);
      tl = 0;
      for (i=n; i>0; i--) {  
        size_t l = tsvalue(top-i)->len;
        memcpy(buffer+tl, tsvalue(top-i)->str, l);
        tl += l;
      }
      tsvalue(top-n) = els_string_newlstr(L, buffer, tl);
    }
    total -= n-1;  
    top -= n-1;
  } while (total > 1);  
}


static void els_Vmcore_pack (els_VmObj *L, Els_vminline_obj firstelem) {
  int i;
  Hash *hunit = els_Unit_new(L, 0);
  for (i=0; firstelem+i<L->top; i++)
    *els_Unit_setint(L, hunit, i+1) = *(firstelem+i);
  
  els_Unit_setstrnum(L, hunit, els_string_new(L, "n"), i);
  L->top = firstelem;  
  ttype(L->top) = ELS_TYPE_UNIT;
  hvalue(L->top) = hunit;
  incr_top;
}


static void adjust_varargs (els_VmObj *L, Els_vminline_obj base, int nfixargs) {
  int nvararg = (L->top-base) - nfixargs;
  if (nvararg < 0)
    els_Heap_adjusttop(L, base, nfixargs);
  els_Vmcore_pack(L, base+nfixargs);
}



#define dojump(pc, i)	{ int d = GETARG_S(i); pc += d; }

extern double pow (double x, double y);
Els_vminline_obj els_Vmcore_execute (els_VmObj *L, const ElsCfunc *cl, Els_vminline_obj base) {
  const els_ir_code *const tf = cl->f.l;
  Els_vminline_obj top;  
  const Instruction *pc = tf->code;
  TString **const kstr = tf->kstr;
  infovalue(base-1)->pc = &pc;
  els_Heap_checkstack(L, tf->maxstacksize+EXTRA_STACK);
  if (tf->is_vararg)  
    adjust_varargs(L, base, tf->numparams);
  else
    els_Heap_adjusttop(L, base, tf->numparams);
  top = L->top;
  
  while(1) {
    const Instruction i = *pc++;
    switch (GET_OPCODE(i)) {
      case BYTECODE_END: {
        L->top = top;
        return top;
      }
      case BYTECODE_RETURN: {
        L->top = top;
        return base+GETARG_U(i);
      }
      case BYTECODE_CALL: {
        int nres = GETARG_B(i);
        if (nres == MULT_RET) nres = ELS_MULTRET;
        L->top = top;
        els_Heap_call(L, base+GETARG_A(i), nres);
        top = L->top;
        break;
      }
      case BYTECODE_TAILCALL: {
        L->top = top;
        els_Heap_call(L, base+GETARG_A(i), ELS_MULTRET);
        return base+GETARG_B(i);
      }
      case BYTECODE_PUSHNULL: {
        int n = GETARG_U(i);
        do {
          ttype(top++) = ELS_TYPE_NULL;
        } while (--n > 0);
        break;
      }
      case BYTECODE_POP: {
        top -= GETARG_U(i);
        break;
      }
      case BYTECODE_PUSHINT: {
        ttype(top) = ELS_TYPE_NUMBER;
        nvalue(top) = (Number)GETARG_S(i);
        top++;
        break;
      }
      case BYTECODE_PUSHSTRING: {
        ttype(top) = ELS_TYPE_STRING;
        tsvalue(top) = kstr[GETARG_U(i)];
        top++;
        break;
      }
      case BYTECODE_PUSHNUM: {
        ttype(top) = ELS_TYPE_NUMBER;
        nvalue(top) = tf->knum[GETARG_U(i)];
        top++;
        break;
      }
      case BYTECODE_PUSHNEGNUM: {
        ttype(top) = ELS_TYPE_NUMBER;
        nvalue(top) = -tf->knum[GETARG_U(i)];
        top++;
        break;
      }
      case BYTECODE_PUSHUPVALUE: {
        *top++ = cl->upvalue[GETARG_U(i)];
        break;
      }
      case BYTECODE_GETLOCAL: {
        *top++ = *(base+GETARG_U(i));
        break;
      }
      case BYTECODE_GETGLOBAL: {
        L->top = top;
        *top = *els_Vmcore_getglobal(L, kstr[GETARG_U(i)]);
        top++;
        break;
      }
      case BYTECODE_GETUNIT: {
        L->top = top;
        top--;
        *(top-1) = *els_Vmcore_getunit(L, top-1);
        break;
      }
      case BYTECODE_GETDOTTED: {
        ttype(top) = ELS_TYPE_STRING;
        tsvalue(top) = kstr[GETARG_U(i)];
        L->top = top+1;
        *(top-1) = *els_Vmcore_getunit(L, top-1);
        break;
      }
      case BYTECODE_GETINDEXED: {
        *top = *(base+GETARG_U(i));
        L->top = top+1;
        *(top-1) = *els_Vmcore_getunit(L, top-1);
        break;
      }
      case BYTECODE_PUSHSELF: {
        StackObj receiver;
        receiver = *(top-1);
        ttype(top) = ELS_TYPE_STRING;
        tsvalue(top++) = kstr[GETARG_U(i)];
        L->top = top;
        *(top-2) = *els_Vmcore_getunit(L, top-2);
        *(top-1) = receiver;
        break;
      }
      case BYTECODE_CREATEUNIT: {
        L->top = top;
        #ifdef ELS_VM_GC_ENABLE
          els_Gc_checkGC(L);
        #endif
        hvalue(top) = els_Unit_new(L, GETARG_U(i));
        ttype(top) = ELS_TYPE_UNIT;
        top++;
        break;
      }
      case BYTECODE_SETLOCAL: {
        *(base+GETARG_U(i)) = *(--top);
        break;
      }
      case BYTECODE_SETGLOBAL: {
        L->top = top;
        els_Vmcore_setglobal(L, kstr[GETARG_U(i)]);
        top--;
        break;
      }
      case BYTECODE_SETUNIT: {
        Els_vminline_obj t = top-GETARG_A(i);
        L->top = top;
        els_Vmcore_setunit(L, t, t+1);
        top -= GETARG_B(i);  
        break;
      }
      case BYTECODE_SETLIST: {
        int aux = GETARG_A(i) * LFIELDS_PER_FLUSH;
        int n = GETARG_B(i);
        Hash *arr = hvalue(top-n-1);
        L->top = top-n;  
        for (; n; n--)
          *els_Unit_setint(L, arr, n+aux) = *(--top);
        break;
      }
      case BYTECODE_SETMAP: {
        int n = GETARG_U(i);
        Els_vminline_obj finaltop = top-2*n;
        Hash *arr = hvalue(finaltop-1);
        L->top = finaltop;  
        for (; n; n--) {
          top-=2;
          *els_Unit_set(L, arr, top) = *(top+1);
        }
        break;
      }
      case BYTECODE_ADD: {
        nvalue(top-2) += nvalue(top-1);
        top--;
        break;
      }
      case BYTECODE_ADDI: {
        if (tonumber(top-1)) {
          ttype(top) = ELS_TYPE_NUMBER;
          nvalue(top) = (Number)GETARG_S(i);
        }
        else
          nvalue(top-1) += (Number)GETARG_S(i);
        break;
      }
      case BYTECODE_SUB: {
        nvalue(top-2) -= nvalue(top-1);
        top--;
        break;
      }
      case BYTECODE_MULT: {
        nvalue(top-2) *= nvalue(top-1);
        top--;
        break;
      }
      case BYTECODE_DIV: {
        nvalue(top-2) /= nvalue(top-1);
        top--;
        break;
      }
      case BYTECODE_POW: {
        nvalue(top-2) = pow(nvalue(top-2),nvalue(top-1));
        top--;
        break;
      }
      case BYTECODE_CONCAT: {
        int n = GETARG_U(i);
        els_Vmcore_strconc(L, n, top);
        top -= n-1;
        L->top = top;
        #ifdef ELS_VM_GC_ENABLE
          els_Gc_checkGC(L);
        #endif
        break;
      }
      case BYTECODE_MINUS: {
        if (tonumber(top-1)) {
          ttype(top) = ELS_TYPE_NULL;
        }
        else
          nvalue(top-1) = -nvalue(top-1);
        break;
      }
      case BYTECODE_NOT: {
        ttype(top-1) =
           (ttype(top-1) == ELS_TYPE_NULL) ? ELS_TYPE_NUMBER : ELS_TYPE_NULL;
        nvalue(top-1) = 1;
        break;
      }
      case BYTECODE_JMPNE: {
        top -= 2;
        if (!els_Object_equalObj(top, top+1)) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPEQ: {
        top -= 2;
        if (els_Object_equalObj(top, top+1)) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPLT: {
        top -= 2;
        if (els_Vmcore_lessthan(L, top, top+1, top+2)) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPLE: {  
        top -= 2;
        if (!els_Vmcore_lessthan(L, top+1, top, top+2)) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPGT: {  
        top -= 2;
        if (els_Vmcore_lessthan(L, top+1, top, top+2)) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPGE: {  
        top -= 2;
        if (!els_Vmcore_lessthan(L, top, top+1, top+2)) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPT: {
        if (ttype(--top) != ELS_TYPE_NULL) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPF: {
        if (ttype(--top) == ELS_TYPE_NULL) dojump(pc, i);
        break;
      }
      case BYTECODE_JMPONT: {
        if (ttype(top-1) == ELS_TYPE_NULL) top--;
        else dojump(pc, i);
        break;
      }
      case BYTECODE_JMPONF: {
        if (ttype(top-1) != ELS_TYPE_NULL) top--;
        else dojump(pc, i);
        break;
      }
      case BYTECODE_JMP: {
        dojump(pc, i);
        break;
      }
      case BYTECODE_PUSHNULLJMP: {
        ttype(top++) = ELS_TYPE_NULL;
        pc++;
        break;
      }
      case BYTECODE_FORPREP: {
        if (tonumber(top-1))
          els_error(L, "错误的循环结构");
        if (tonumber(top-2))
          els_error(L, "错误的循环结构");
        if (tonumber(top-3))
          els_error(L, "错误的循环结构");
        if (nvalue(top-1) > 0 ?
            nvalue(top-3) > nvalue(top-2) :
            nvalue(top-3) < nvalue(top-2)) {  
          top -= 3;  
          dojump(pc, i);  
        }
        break;
      }
      case BYTECODE_FORLOOP: {
        if (ttype(top-3) != ELS_TYPE_NUMBER)
          els_error(L, "错误的循环结构");
        nvalue(top-3) += nvalue(top-1);  
        if (nvalue(top-1) > 0 ?
            nvalue(top-3) > nvalue(top-2) :
            nvalue(top-3) < nvalue(top-2))
          top -= 3;  
        else
          dojump(pc, i);  
        break;
      }
      case BYTECODE_LFORPREP: {
        Node *node;
        if (ttype(top-1) != ELS_TYPE_UNIT)
          els_error(L, "错误的循环结构");
        node = els_Unit_next(L, hvalue(top-1), &els_Object_nullobject);
        if (node == NULL) {  
          top--;  
          dojump(pc, i);  
        }
        else {
          top += 2;  
          *(top-2) = *key(node);
          *(top-1) = *val(node);
        }
        break;
      }
      case BYTECODE_LFORLOOP: {
        Node *node;
        node = els_Unit_next(L, hvalue(top-3), top-2);
        if (node == NULL)  
          top -= 3;  
        else {
          *(top-2) = *key(node);
          *(top-1) = *val(node);
          dojump(pc, i);  
        }
        break;
      }
      case BYTECODE_CLOSURE: {
        L->top = top;
        els_Vmcore_L_csfunciotn(L, tf->kcodeir[GETARG_A(i)], GETARG_B(i));
        top = L->top;
        #ifdef ELS_VM_GC_ENABLE
          els_Gc_checkGC(L);
        #endif
        break;
      }
    }
  }
}

static int zmfilbuf (vm_iobuff* z) {
  (void)z;  
  return EOZ;
}

vm_iobuff* els_vmio_mopen (vm_iobuff* z, const char* b, size_t size, const char *name) {
  if (b==NULL) return NULL;
  z->n = size;
  z->p = (const unsigned char *)b;
  z->filbuf = zmfilbuf;
  z->u = NULL;
  z->name = name;
  return z;
}

vm_iobuff* els_vmio_sopen (vm_iobuff* z, const char* s, const char *name) {
  if (s==NULL) return NULL;
  return els_vmio_mopen(z, s, strlen(s), name);
}

static int zffilbuf (vm_iobuff* z) {
  size_t n;
  if (feof((FILE *)z->u)) return EOZ;
  n = fread(z->buffer, 1, ELS_VMIO_SIZE, (FILE *)z->u);
  if (n==0) return EOZ;
  z->n = n-1;
  z->p = z->buffer;
  return *(z->p++);
}

vm_iobuff* els_vmio_Fopen (vm_iobuff* z, FILE* f, const char *name) {
  if (f==NULL) return NULL;
  z->n = 0;
  z->p = z->buffer;
  z->filbuf = zffilbuf;
  z->u = f;
  z->name = name;
  return z;
}

size_t els_vmio_read (vm_iobuff *z, void *b, size_t n) {
  while (n) {
    size_t m;
    if (z->n == 0) {
      if (z->filbuf(z) == EOZ)
        return n;  
      els_vmio_ugetc(z);  
    }
    m = (n <= z->n) ? n : z->n;  
    memcpy(b, z->p, m);
    z->n -= m;
    z->p += m;
    b = (char *)b + m;
    n -= m;
  }
  return 0;
}

